package com.example.springboot4;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.IFill;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.builder.Entity;
import com.baomidou.mybatisplus.generator.config.querys.MySqlQuery;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.fill.Column;
import com.baomidou.mybatisplus.generator.fill.Property;
import com.baomidou.mybatisplus.generator.keywords.MySqlKeyWordsHandler;
import com.example.springboot4.system.config.MySqlTypeConvertCustom;
import com.example.springboot4.system.model.BaseEntity;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Mapper;

import java.util.*;

/**
 * 代码生成工具
 *
 * @author 宋义州
 * @since 2023/9/20 16:28
 */
public class CodeGenerator {

    private static final String url = "jdbc:mysql://127.0.0.1:3306/textbooks?allowMultiQueries=true&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false";

    private static final String username = "root";

    private static final String password = "syz@2024!";

    /**
     * 实体类父类
     */
    private static final Class<?> superEntityClass = BaseEntity.class;

    /**
     * 父类公共字段,
     * 这里配置的数据库字段在生成的实体类的时候会被忽略
     */
    private static final String[] superEntityColumns = new String[]{
            "create_time", "create_user", "update_time", "update_user"
    };

    /**
     * 自动填充配置 数据库字段名称;
     * <p>和下面的实体字段使用效果一样，只是一个是数据库字段、一个是实体类字段、二选一就行了
     * <p>具体的填充还是要通过拦截器去实现
     * <p>创建MetaHandler类实现MetaObjectHandler接口、重写insertFill和updateFill方法
     * <p>配置MyBatisPlusConfig.java
     * <p>初始化mybatis-plus的GlobalConfig配置 自动填充功能
     */
    private static final List<IFill> tableFills = Arrays.asList(
            new Column("create_time", FieldFill.INSERT),
            new Column("create_user", FieldFill.INSERT)
    );

    /**
     * 自动填充配置 实体类字段名称
     * <p>和上面的数据库字段使用效果一样，只是一个是数据库字段、一个是实体类字段
     * <p>二选一就行了
     */
    private static final List<IFill> entityFills = Arrays.asList(
            new Property("updateTime", FieldFill.UPDATE),
            new Property("updateUser", FieldFill.UPDATE)
    );

    /**
     * 逻辑删除字段（数据库字段名称）
     */
    private static final String logicDelete = "";

    /**
     * 要生成的表
     */
    private static String[] tables = new String[]{
            "sys_user"
    };

    /**
     * 过滤表前缀
     */
    private static final String[] tablePrefix = new String[]{
            "tb_",
            "sys_"
    };

    /**
     * 父包名
     */
    private static final String packageName = "com.example.springboot4";

    /**
     * 模块名 XXX模块，可以将模块功能代码分成一个个单独的文件夹存放
     */
    private static String modelName = "user";

    /**
     * 作者
     */
    private static final String author = "宋义州";

    /**
     * 是否生成文档（swagger、springdoc）、为空则不生成
     */
    public static String doc = "springdoc";

    /**
     * 实体类包名前缀 model.xxx
     * <p>例如：
     * <p>model.entity
     * <p>     model.vo
     * <p>     model.dto
     */
    private static final String modelPackagePrefix = "";

    public static void main(String[] args) {

        FastAutoGenerator.create(
                        // 数据库配置
                        new DataSourceConfig.Builder(url, username, password)
                                .dbQuery(new MySqlQuery())
                                // 数据库类型转换器
                                .typeConvert(new MySqlTypeConvertCustom())
                                .keyWordsHandler(new MySqlKeyWordsHandler()))
                // 全局配置(GlobalConfig)
                .globalConfig(builder -> {
                    // 设置作者
                    builder.author(author);
                    //开启 swagger 模式 默认值:false
                    if (StringUtils.isNotBlank(doc) && Objects.equals("swagger", doc)) {
                        builder.enableSwagger();
                    }
                    //开启 springdoc 模式 默认值:false
                    if (StringUtils.isNotBlank(doc) && Objects.equals("springdoc", doc)) {
                        builder.enableSpringdoc();
                    }

                    builder.disableOpenDir() //禁止打开输出目录 默认值:true
                            .commentDate("yyyy-MM-dd HH:mm:ss") //注释日期
                            .dateType(DateType.TIME_PACK) //定义生成的实体类中日期类型 默认值: DateType.TIME_PACK
                            .outputDir(System.getProperty("user.dir") + "/src/main/java");// 指定输出目录 默认值: windows:D:// linux or mac : /tmp
                })
                // 包配置(PackageConfig)
                .packageConfig(builder -> {
                    builder.parent(packageName); // 父包名
                    // 设置父包模块名 默认值:无
                    if (StringUtils.isNotBlank(modelName)) {
                        builder.moduleName(modelName);
                    }
                    builder.controller("controller")   // Controller 包名 默认值:controller
                            .entity(modelPackagePrefix + "entity")           // Entity 包名 默认值:entity
                            .service("service")         // Service 包名 默认值:service
                            .serviceImpl("service.impl")// impl包名 默认值:service.impl
                            .mapper("mapper");   // Mapper 包名 默认值:mapper
                    //.pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir") + "/src/main/resources/mapper")) // 设置mapperXml生成路径 默认存放在mapper的xml
                })
                // 注入配置(InjectionConfig)
                .injectionConfig(consumer -> {
                    consumer.beforeOutputFile((tableInfo, objectMap) -> System.out.println("tableInfo: " + tableInfo.getEntityName() + " objectMap: " + objectMap.size()));
                    Map<String, Object> customMap = new HashMap<>();
                    modelName = StringUtils.isBlank(modelName) ? "" : "." + modelName;
                    customMap.put("DTO", packageName + modelName + "." + modelPackagePrefix + "dto");
                    customMap.put("VO", packageName + modelName + "." + modelPackagePrefix + "vo");
                    consumer.customMap(customMap);
                    consumer.customFile(
                            Arrays.asList(
                                    new CustomFile.Builder().fileName("Dto.java").templatePath("/templates/dto.java.vm")
                                            .packageName(modelPackagePrefix + "dto").enableFileOverride().build(),
                                    new CustomFile.Builder().fileName("QueryDto.java").templatePath("/templates/queryDto.java.vm")
                                            .packageName(modelPackagePrefix + "dto").enableFileOverride().build(),
                                    new CustomFile.Builder().fileName("Vo.java").templatePath("/templates/vo.java.vm")
                                            .packageName(modelPackagePrefix + "vo").enableFileOverride().build()
                            ));
                })
                // 策略配置(StrategyConfig)
                .strategyConfig(builder -> {
                    // 设置需要生成的表名
                    if (ArrayUtils.isEmpty(tables)) {
                        // 如果无法输入请修改idea配置：help -> Edit Custom VM Options -> 最后追加：-D editable.java.test.console=true  -> 重启idea
                        Scanner scanner = new Scanner(System.in);
                        System.out.println("请输入需要生成的表名,多个用逗号隔开:");
                        String in = scanner.next();
                        scanner.close();
                        tables = in.split(",");
                    }
                    builder.addInclude(tables);
                    // 设置过滤表前缀
                    if (ArrayUtils.isNotEmpty(tablePrefix)) {
                        builder.addTablePrefix(tablePrefix);
                    }
                    // 实体类策略配置
                    Entity.Builder entityBuilder = builder.entityBuilder();
                    // 设置实体类模板
                    entityBuilder.javaTemplate("/templates/entity.java.vm");
                    // 设置实体类的父类
                    if (Objects.nonNull(superEntityClass)) {
                        entityBuilder.superClass(superEntityClass);
                        // 添加父类公共字段
                        if (ArrayUtils.isNotEmpty(superEntityColumns)) {
                            entityBuilder.addSuperEntityColumns(superEntityColumns);
                        }
                    }

                    //entityBuilder.idType(IdType.ASSIGN_ID)//主键策略  如果是自增的话可以不用设置、默认是自增

                    // 自动填充配置 数据库字段名称
                    if (CollectionUtils.isNotEmpty(tableFills)) {
                        entityBuilder.addTableFills(tableFills);
                    }
                    // 自动填充配置 实体类字段名称
                    if (CollectionUtils.isNotEmpty(entityFills)) {
                        entityBuilder.addTableFills(entityFills);
                    }

                    //.disableSerialVersionUID() //禁止生成serialVersionUID
                    entityBuilder.enableLombok() //开启lombok
                            .enableChainModel() //开启链式模型
                            .enableRemoveIsPrefix(); //开启移除is前缀
                    // 说明逻辑删除是哪个字段
                    if (StringUtils.isNotBlank(logicDelete)) {
                        entityBuilder.logicDeleteColumnName(logicDelete);
                    }
                    entityBuilder.enableTableFieldAnnotation()// 属性加上注解说明
                            .enableFileOverride(); // 开启覆盖已有文件

                    //controller 策略配置
                    builder.controllerBuilder()
                            .template("/templates/controller.java")
                            .formatFileName("%sController")
                            .enableRestStyle() // 开启RestController注解
                            .enableHyphenStyle() // 开启驼峰转连字符
                            .enableFileOverride(); // 开启覆盖已有文件
                    // service策略配置
                    builder.serviceBuilder()
                            .serviceTemplate("/templates/service.java")
                            .serviceImplTemplate("/templates/serviceImpl.java")
                            .formatServiceFileName("%sService")
                            .formatServiceImplFileName("%sServiceImpl")
                            .enableFileOverride(); // 开启覆盖已有文件
                    // mapper策略配置
                    builder.mapperBuilder()
                            .mapperTemplate("/templates/mapper.java")
                            .mapperXmlTemplate("/templates/mapper.xml")
                            .formatMapperFileName("%sMapper")
                            .mapperAnnotation(Mapper.class)//mapper注解
                            .formatXmlFileName("%sMapper")
                            .enableBaseColumnList()// 开启BaseColumnList
                            .enableBaseResultMap()// 开启BaseResultMap
                            .enableFileOverride(); // 开启覆盖已有文件
                })
                //.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板，默认的是Velocity引擎模板
                .execute();
    }

}
